Simulate reservoirs within the distributed hydrological model
!! Simulate reservoirs within the distributed hydrological model !|author: <a href="mailto:giovanni.ravazzani@polimi.it">Giovanni Ravazzani</a> ! license: <a href="http://www.gnu.org/licenses/">GPL</a> ! !### History ! ! current version 2.0 - 4th March 2025 ! ! | version | date | comment | ! |----------|-------------|----------| ! | 1.0 | 05/Oct/2016 | Original code | ! | 1.1 | 08/Feb/2023 | OutReservoirsInit and OutReservoirs added | ! | 1.2 | 17/Mar/2023 | bypass channel simulation included | ! | 1.3 | 28/Nov/2023 | manage reservoir when level is high | ! | 1.4 | 12/Dec/2023 | eflow is a vector of 365 daily value | ! | 1.5 | 26/Mar/2024 | reservoir volume written to output | ! | 1.6 | 10/Apr/2024 | observed reservoir downstream discharge read from file | ! | 1.7 | 11/Apr/2024 | observed reservoir diverted discharge read from file | ! | 1.8 | 05/May/2024 | Qin and Qout channel written even in reservoir without diversion | ! | 1.9 | 04/Sep/2024 | modified code to save and read reservoir state variables | ! | 2.0 | 04/Mar/2025 | one outlet discharge rule can be assigned for every day of the year | ! !### License ! license: GNU GPL <http://www.gnu.org/licenses/> ! !### Module Description ! This module includes data and routines to manage ! reservoirs within the distributed hydrological model ! Two types of reservoirs are available, _on-stream_ and ! _off-stream_ reservoirs. ! _On-stream_ reservoirs are the ones that are located ! across a river. An _off-stream_ reservoir is a reservoir ! that is not located on a streambed, and is supplied by ! an artificial canal or pipeline. In case of _off-stream_ ! reservoir, the water stored can be released in a ! downstream section of the same river from which water ! was withdrawn, or in a different water course. ! A _diversion channel_ can be associated to a given ! reservoir. In this case, the _diversion channel_ ! diverts water from the reservoir and conveys it ! to a downstream section of the same river (bypass) ! or to a different river. The _diversion channel_ ! is an hydraulic structure typically used to ! diverts flow to hydroelectric power plant. ! MODULE Reservoirs ! Modules used: USE DataTypeSizes, ONLY : & ! Imported Parameters: float, short USE Chronos, ONLY : & !Imported definitions: DateTime, & !Imported variables: timeString, & !Imported operators: ASSIGNMENT (=) USE ObservationalNetworks, ONLY : & ! Imported definitions: ObservationalNetwork, & !Imported routines: ReadMetadata USE TableLib, ONLY : & !Imported definitions: Table, & TableCollection, & !Imported routines: TableNew, & TableGetNrows, & TableGetValue, & TableSetId, & TableSetTitle, & TableSetRowCol, & TableSetColHeader, & TableSetColUnit, & TableFillRow, & TableExport USE Utilities, ONLY : & !Imported routines: GetUnit USE IniLib, ONLY: & !Imported derived types: IniList, & !Imported routines: IniOpen, & IniClose, & IniReadReal, & IniReadString, & SectionIsPresent, & KeyIsPresent, & IniReadInt, & SubSectionIsPresent USE Loglib, ONLY : & !Imported routines: Catch USE StringManipulation, ONLY : & !Imported routines: ToString, & StringToFloat, & StringCompact, & StringToUpper, & StringTokenize, & StringToShort USE GridLib, ONLY : & !Imported definitions: grid_real, & grid_integer USE GeoLib, ONLY : & !Imported definitions: Coordinate, & !imported routines: DecodeEpsg USE GridOperations, ONLY : & !Imported routines: GetIJ USE Diversions, ONLY : & !Imported definition: Diversion IMPLICIT NONE ! Global (i.e. public) Declarations: TYPE Reservoir INTEGER(KIND = short) :: id INTEGER(KIND = short) :: rk !!runge-kutta order CHARACTER(LEN=100) :: name CHARACTER(len=10) :: typ !! on-stream off-stream by-pass TYPE (Coordinate) :: xyz !!easting, northing and elevation in real world INTEGER(KIND = short) :: r !!cell row i INTEGER(KIND = short) :: c !!cell column j INTEGER(KIND = short) :: rout !!cell row where off-stream pool outflow is discharged INTEGER(KIND = short) :: cout !!cell column where off-stream pool outflow is discharged REAL(KIND = float) :: stage !!current stage [m] REAL(KIND = float) :: stageMax !!maximum stage [m] REAL(KIND = float) :: stageTarget !!follow a target (observed) stage [m] INTEGER(KIND = short) :: typOut !!type ofoutflow: 1=free flow 2=target level INTEGER(KIND = short) :: unit !!file unit target stage INTEGER(KIND = short) :: unitDischargeDownstream !!file unit of observed downstream discharge INTEGER(KIND = short) :: unitDischargeDiverted !!file unit of observed diverted discharge LOGICAL :: dischargeDownstream !!read observed downstream discharge when true LOGICAL :: dischargeDiverted !!read observed diverted discharge when true INTEGER(KIND = short) :: fileunit_out !!file unit for writing results TYPE(ObservationalNetwork) :: network ! pseudo station network of target file TYPE(ObservationalNetwork) :: networkDischargeDownstream ! pseudo station network of downstream discharge TYPE(ObservationalNetwork) :: networkDischargeDiverted ! pseudo station network of diverted discharge TYPE(DateTime) :: tReadNewStage TYPE(DateTime) :: tReadNewDischargeDownstream TYPE(DateTime) :: tReadNewDischargeDiverted REAL(KIND = float) :: Qout ! [m3/s] REAL(KIND = float) :: Qout_off ! off-stream pool outflow [m3/s] REAL(KIND = float) :: Pout_off ! off-stream pool outflow of previous time step [m3/s] REAL(KIND = float) :: eFlow (365) !daily environmental flow [m3/s] REAL(KIND = float) :: freeFlow ![m3/s] when Qin < freeFlow and stage <= freeFlowElevation Qout = Qin REAL(KIND = float) :: freeFlowElevation !free flow elevation [m] TYPE(Table) :: geometry !reservoir geometry and stage-discharge relationship TYPE(Table) :: weir !used by off-stream reservoirs and bypass channels INTEGER(KIND = short) :: weirDOY (365) !weir function used for a given doy INTEGER(KIND = short) :: geometryDOY (365) !geometry function used for a given doy REAL(KIND = float) :: xout !! x coordinate where outflow from reservoir is discharged REAL(KIND = float) :: yout !! y coordinate where outflow from reservoir is discharged LOGICAL :: highLevel !! true when reservoir is managed for high level REAL(KIND = float) :: fullReservoirLevel !! full reservoir level (m) INTEGER :: qoutRule !!determines how to compute Qout LOGICAL :: rising !!override Qout calculation only when Qin discharge is rising TYPE (Diversion) :: bypass !!diversion channel LOGICAL :: bypassIsPresent TYPE (Reservoir), POINTER :: next !dynamic list END TYPE Reservoir !Public routines PUBLIC :: InitReservoirs PUBLIC :: GetReservoirById PUBLIC :: CountReservoirs PUBLIC :: OutReservoirsInit PUBLIC :: ReservoirSaveStatus !Public variables INTEGER (KIND = short) :: dtReservoir TYPE (Reservoir), POINTER :: pools !Local declarations: !Local routines PRIVATE :: ReservoirReadStatus PRIVATE :: SetDailyArray PRIVATE :: ReadGeometry PRIVATE :: ReadWeir !Local parameters INTEGER, PARAMETER, PRIVATE :: QIN = 1 !Local variables: INTEGER (KIND = short), PRIVATE :: nReservoirs !total number of reservoirs INTEGER (KIND = short), PRIVATE :: nReservoirsWithDiversion ! number of reservoirs with diversion !======= CONTAINS !======= ! Define procedures contained in this module. !============================================================================== !| Description: ! Initialize reservoirs SUBROUTINE InitReservoirs & ! (filename_ini,tbegin, mask, list) IMPLICIT NONE !arguments with intent in: CHARACTER ( LEN = * ), INTENT(IN) :: filename_ini TYPE (DateTime), INTENT(IN) :: tbegin TYPE (grid_integer), INTENT(IN) :: mask !arguments with intent out: TYPE(Reservoir),POINTER,INTENT(out) :: list !list of reservoirs !local declarations CHARACTER(LEN=1000) :: filename LOGICAL :: hotstart TYPE(reservoir),POINTER :: currentReservoir !points to current reservoir INTEGER(KIND = short) :: id_res !reservoir id INTEGER(KIND = short) :: k, i, j TYPE(IniList) :: iniDB INTEGER(KIND = short) :: fileunit_geometry INTEGER(KIND = short) :: fileunit_weir CHARACTER (LEN = 300) :: string LOGICAL :: error INTEGER(KIND = short) :: err_io INTEGER(KIND = short), ALLOCATABLE :: doy (:) INTEGER (KIND = short) :: nDOY INTEGER (KIND = short) :: shortInt LOGICAL :: isString !-------------------------------end of declaration----------------------------- CALL Catch ('info', 'Reservoirs', 'Initializing reservoirs ') !-------------------------------------------- ! open and read configuration file !-------------------------------------------- CALL IniOpen (filename_ini, iniDB) !-------------------------------------------- ! hot / cold start !-------------------------------------------- IF ( KeyIsPresent ('path-hotstart', iniDB ) ) THEN hotstart = .TRUE. ELSE hotstart = .FALSE. END IF !-------------------------------------------- ! allocate variables !-------------------------------------------- nReservoirs = IniReadInt ('nreservoirs', iniDB) nReservoirsWithDiversion = 0 !prepare list of reservoirs NULLIFY (list) DO k = 1, nReservoirs IF (.NOT. ASSOCIATED (list) ) THEN ALLOCATE (list) currentReservoir => list ELSE ALLOCATE (currentReservoir % next) currentReservoir => currentReservoir % next END IF !id currentReservoir % id = IniReadInt ('id', iniDB, section = ToString(k)) !reservoir type: on-stream, off-stream currentReservoir % typ = & IniReadString ('type', iniDB, section = ToString(k)) !name currentReservoir % name = & IniReadString ('name', iniDB, section = ToString(k)) !coordinate currentReservoir % xyz % easting = & IniReadReal ('easting', iniDB, section = ToString(k)) currentReservoir % xyz % northing = & IniReadReal ('northing', iniDB, section = ToString(k)) currentReservoir % xyz % system = DecodeEpsg (IniReadInt ('epsg', iniDB)) !local coordinate CALL GetIJ ( X = currentReservoir % xyz % easting, & Y = currentReservoir % xyz % northing, & grid = mask, i = currentReservoir % r, & j = currentReservoir % c ) !runge-kutta order currentReservoir % rk = IniReadInt ('rk', iniDB, section = ToString(k)) !initial stage IF (.NOT. hotstart) THEN currentReservoir % stage = & IniReadReal ('stage', iniDB, section = ToString(k)) END IF SELECT CASE ( currentReservoir % typ ) CASE ( 'on' ) !on-stream detention basin !target stage currentReservoir % eFlow = 0. IF (KeyIsPresent ('stage-target-file', iniDB, section = ToString(k) ) ) THEN string = IniReadString ('stage-target-file', iniDB, section = ToString(k)) currentReservoir % unit = GetUnit () OPEN ( unit=currentReservoir % unit , file=string) currentReservoir % typOut = 2 currentReservoir % tReadNewStage = tbegin CALL ReadMetadata (currentReservoir % unit, currentReservoir % network ) !environmental flow string = IniReadString ('e-flow', iniDB, section = ToString(k)) currentReservoir % eFlow = SetDailyArray (string) ELSE currentReservoir % typOut = 1 END IF !discharge downstream file IF (KeyIsPresent ('discharge-downstream-file', iniDB, & section = ToString(k) ) ) THEN string = IniReadString ('discharge-downstream-file', iniDB, & section = ToString(k)) currentReservoir % unitDischargeDownstream = GetUnit () OPEN ( unit = currentReservoir % unitDischargeDownstream , & file = string) currentReservoir % tReadNewDischargeDownstream = tbegin CALL ReadMetadata (currentReservoir % unitDischargeDownstream, & currentReservoir % networkDischargeDownstream ) currentReservoir % dischargeDownstream = .TRUE. ELSE currentReservoir % dischargeDownstream = .FALSE. END IF !discharge diverted file IF (KeyIsPresent ('discharge-diverted-file', iniDB, & section = ToString(k) ) ) THEN string = IniReadString ('discharge-diverted-file', iniDB, & section = ToString(k)) currentReservoir % unitDischargeDiverted = GetUnit () OPEN ( unit = currentReservoir % unitDischargeDiverted , & file = string) currentReservoir % tReadNewDischargeDiverted = tbegin CALL ReadMetadata (currentReservoir % unitDischargeDiverted, & currentReservoir % networkDischargeDiverted ) currentReservoir % dischargeDiverted = .TRUE. ELSE currentReservoir % dischargeDiverted = .FALSE. END IF !free flow IF ( KeyIsPresent (key = 'free-flow', iniDB = iniDB, & section = ToString(k) ) ) THEN currentReservoir % freeFlow = IniReadReal ('free-flow', iniDB, & section = ToString(k) ) ELSE currentReservoir % freeFlow = 0. END IF !free flow elevation IF ( KeyIsPresent (key = 'free-flow-elevation', iniDB = iniDB, & section = ToString(k) ) ) THEN currentReservoir % freeFlowElevation = & IniReadReal ('free-flow-elevation', iniDB, & section = ToString(k) ) ELSE currentReservoir % freeFlowElevation = 0. END IF !geometry CALL ReadGeometry (iniDB, k, currentReservoir ) !manage high level options IF ( SubSectionIsPresent ('manage-high-level',ToString(k), iniDB ) ) THEN currentReservoir % highLevel = .TRUE. !read full reservoir level currentReservoir % fullReservoirLevel = & IniReadReal ( 'full-reservoir-level', iniDB, ToString(k), & 'manage-high-level') !read how to compute qout string = IniReadString ('qout', iniDB, ToString(k), & 'manage-high-level') SELECT CASE ( StringToUpper(string) ) CASE ('QIN') currentReservoir % qoutRule = QIN END SELECT currentReservoir % rising = & IniReadInt ( 'rising', iniDB, ToString(k), 'manage-high-level') ELSE currentReservoir % highLevel = .FALSE. END IF CASE ( 'off' ) !off-stream detention basin !maximum stage currentReservoir % stageMax = StringToFloat (string, error) currentReservoir % typOut = 1 !geometry string = IniReadString ('geometry', iniDB, section = ToString(k)) CALL TableNew (string, currentReservoir % geometry) !weir string = IniReadString ('weir', iniDB, section = ToString(k)) CALL TableNew (string, currentReservoir % weir) !read x and y coordinate where outflow from reservoir is discharged currentReservoir % xout = & IniReadReal ('xout', iniDB, section = ToString(k)) currentReservoir % yout = & IniReadReal ('yout', iniDB, section = ToString(k)) CALL GetIJ ( X = currentReservoir % xout, & Y = currentReservoir % yout, & grid = mask, i = currentReservoir % rout, & j = currentReservoir % cout ) CASE DEFAULT CALL Catch ('error', 'Reservoirs', & 'unknown reservoir type: ', argument = currentReservoir % typ ) END SELECT !diversion channel (optional) IF ( SubSectionIsPresent ('diversion',ToString(k), iniDB ) ) THEN currentReservoir % bypassIsPresent = .TRUE. nReservoirsWithDiversion = nReservoirsWithDiversion + 1 !read x and y coordinate where outflow from reservoir is discharged currentReservoir % bypass % xout = & IniReadReal ('xout', iniDB, section = ToString(k), & subsection = 'diversion') currentReservoir % bypass % yout = & IniReadReal ('yout', iniDB, section = ToString(k), & subsection = 'diversion') !local coordinate in raster reference system CALL GetIJ ( X = currentReservoir % bypass % xout, & Y = currentReservoir % bypass % yout, & grid = mask, i = currentReservoir % bypass % rout, & j = currentReservoir % bypass % cout ) !read weir data CALL ReadWeir (iniDB, k, currentReservoir % bypass ) !channel lenght currentReservoir % bypass % channelLenght = & IniReadReal ('channel-lenght', iniDB, section = ToString(k), & subsection = 'diversion' ) !channel slope currentReservoir % bypass % channelSlope = & IniReadReal ('channel-slope', iniDB, section = ToString(k), & subsection = 'diversion' ) !channel roughness coefficient currentReservoir % bypass % channelManning = & IniReadReal ('channel-manning', iniDB, section = ToString(k), & subsection = 'diversion' ) !channel section bottom width currentReservoir % bypass % channelWidth = & IniReadReal ('section-bottom-width', iniDB, section = ToString(k), & subsection = 'diversion' ) !channel section bank slope currentReservoir % bypass % channelBankSlope = & IniReadReal ('section-bank-slope', iniDB, section = ToString(k), & subsection = 'diversion' ) !environmental flow IF ( KeyIsPresent ('e-flow', iniDB, section = ToString(k), & subsection = 'diversion' ) ) THEN string = IniReadString ('e-flow', iniDB, section = ToString(k), & subsection = 'diversion' ) currentReservoir % bypass % eFlow = SetDailyArray (string) ELSE !e-flow = 0. currentReservoir % bypass % eFlow = 0. END IF ELSE currentReservoir % bypassIsPresent = .FALSE. END IF NULLIFY (currentReservoir % next) END DO IF (hotstart) THEN string = IniReadString ('path-hotstart', iniDB) CALL ReservoirReadStatus (string) END IF !-------------------------------------------- ! close configuration file !-------------------------------------------- CALL IniClose (iniDb) RETURN END SUBROUTINE InitReservoirs !============================================================================== !| Description: ! initialise files for output SUBROUTINE OutReservoirsInit & ! (list, path_out) IMPLICIT NONE !arguments with intent(in): TYPE(Reservoir), POINTER, INTENT(IN) :: list !list of reservoirs CHARACTER ( LEN = *), INTENT(IN) :: path_out !local declarations: TYPE(Reservoir), POINTER :: currentReservoir !current reservoir in alist CHARACTER (len = 100) :: string !------------------------------end of declarations----------------------------- currentReservoir => list DO WHILE (ASSOCIATED(currentReservoir)) !loop trough all reservoirs string = ToString (currentReservoir % id) currentReservoir % fileunit_out = GetUnit () OPEN(currentReservoir % fileunit_out,& file = path_out (1:LEN_TRIM(path_out))//'reservoir_'//& TRIM(string)//'.out') WRITE(currentReservoir % fileunit_out,'(a)') 'FEST: reservoir routing' WRITE(currentReservoir % fileunit_out,'(a,a)') & 'reservoir name: ', currentReservoir % name & (1:LEN_TRIM(currentReservoir % name)) IF ( currentReservoir % typ == 'off' ) THEN WRITE(currentReservoir % fileunit_out,'(a)') 'reservoir type: offstream' ELSE WRITE(currentReservoir % fileunit_out,'(a)') 'reservoir type: onstream' END IF IF ( currentReservoir % bypassIsPresent ) THEN WRITE(currentReservoir % fileunit_out,'(a)') 'with diversion: yes' ELSE WRITE(currentReservoir % fileunit_out,'(a)') 'with diversion: no' END IF WRITE(currentReservoir % fileunit_out,'(a,i4)') 'reservoir id: ', currentReservoir % id WRITE(currentReservoir % fileunit_out,*) WRITE(currentReservoir % fileunit_out,'(a)')'data' SELECT CASE ( currentReservoir % typ ) CASE ( 'off' ) IF ( currentReservoir % bypassIsPresent ) THEN WRITE(currentReservoir % fileunit_out,'(A)') 'DateTime h[m] Volume[m3] & Qupstream[m3/s] Qdownstream[m3/s] & QinChannel[m3/s] QoutChannel[m3/s]' ELSE WRITE(currentReservoir % fileunit_out,'(A)') 'DateTime h[m] Volume[m3] & Qupstream[m3/s] Qdownstream[m3/s]' END IF CASE ( 'on' ) WRITE(currentReservoir % fileunit_out,'(A)') 'DateTime h[m] Volume[m3] & Qupstream[m3/s] Qdownstream[m3/s] & QinChannel[m3/s] QoutChannel[m3/s]' END SELECT currentReservoir => currentReservoir % next END DO RETURN END SUBROUTINE OutReservoirsInit !============================================================================== !| Description: ! write results on files SUBROUTINE OutReservoirs & ! (list, time, Qin, Qout) IMPLICIT NONE !arguments with intent(in): TYPE (Reservoir), POINTER, INTENT(IN) :: list !list of reservoirs TYPE (DateTime), INTENT(IN) :: time TYPE (grid_real), INTENT(IN) :: Qin TYPE (grid_real), INTENT(IN) :: Qout !local declarations: TYPE(Reservoir), POINTER :: currentReservoir !current reservoir in alist REAL (KIND = float) :: volume !water stored in reservoir (m3) REAL (KIND = float) :: wse !water surface elevation !------------------------------end of declarations----------------------------- timeString = time currentReservoir => list DO WHILE (ASSOCIATED(currentReservoir)) !loop trough all reservoirs wse = currentReservoir % stage CALL TableGetValue ( valueIn = wse, tab = currentReservoir % geometry, & keyIn = 'h', keyOut ='volume', & match = 'linear', valueOut = volume, & bound = 'extendlinear' ) SELECT CASE ( currentReservoir % typ) CASE ( 'off' ) IF ( currentReservoir % bypassIsPresent ) THEN WRITE(currentReservoir % fileunit_out,'(a,6(" ",e14.7) )') timeString,& currentReservoir % stage, volume, & Qin % mat(currentReservoir % r, currentReservoir % c),& Qout % mat(currentReservoir % r, currentReservoir % c), & currentReservoir % bypass % QinChannel, & currentReservoir % bypass % QoutChannel ELSE WRITE(currentReservoir % fileunit_out,'(a,4(" ",e14.7) ) ') timeString,& currentReservoir % stage, volume, & Qin % mat(currentReservoir % r, currentReservoir % c),& Qout % mat(currentReservoir % r, currentReservoir % c) END IF CASE ( 'on' ) IF ( currentReservoir % bypassIsPresent ) THEN WRITE(currentReservoir % fileunit_out,'(a,6(" ",e14.7) )') timeString,& currentReservoir % stage, volume, & Qin % mat(currentReservoir % r, currentReservoir % c),& Qout % mat(currentReservoir % r, currentReservoir % c), & currentReservoir % bypass % QinChannel, & currentReservoir % bypass % QoutChannel ELSE WRITE(currentReservoir % fileunit_out,'(a,6(" ",e14.7) )') timeString,& currentReservoir % stage, volume, & Qin % mat(currentReservoir % r, currentReservoir % c),& Qout % mat(currentReservoir % r, currentReservoir % c), & 0.0, 0.0 END IF END SELECT currentReservoir => currentReservoir % next END DO RETURN END SUBROUTINE OutReservoirs !============================================================================== !| Description: ! Save reservoirs status into file SUBROUTINE ReservoirSaveStatus & ! ( pathOut, time ) IMPLICIT NONE !arguments with intent(in): CHARACTER ( LEN = *), INTENT (IN) :: pathOut TYPE (DateTime), OPTIONAL, INTENT (IN) :: time ! local declarations: TYPE (Table) :: tab CHARACTER (LEN = 300) :: string CHARACTER (LEN = 100), ALLOCATABLE :: row (:) INTEGER (KIND = short) :: i TYPE (Reservoir), POINTER :: currentReservoir !points to current reservoir !-------------------------------end of declaration----------------------------- !create new table for reservoir stage CALL TableNew ( tab ) !populate table string = 'reservoir stage' CALL TableSetId ( tab, string) IF ( PRESENT (time) ) THEN timeString = time string = 'reservoir stage at: ' // timeString ELSE string = 'reservoir stage at the end of simulation' END IF CALL TableSetTitle ( tab, string) !Allocate variables CALL TableSetRowCol ( tab, nReservoirs, 2 ) IF ( ALLOCATED ( row ) ) THEN DEALLOCATE ( row ) END IF ALLOCATE ( row (2) ) !set column header and unit CALL TableSetColHeader (tab, 1, 'id') CALL TableSetColHeader (tab, 2, 'stage') CALL TableSetColUnit (tab, 1, '-') CALL TableSetColUnit (tab, 2, 'm') !fill in rows currentReservoir => pools DO i = 1, nReservoirs !id row (1) = ToString (currentReservoir % id) !stage row (2) = ToString (currentReservoir % stage) currentReservoir => currentReservoir % next CALL TableFillRow (tab, i, row) END DO IF (PRESENT(time)) THEN timeString = time timeString = timeString (1:19) // '_' timeString (14:14) = '-' timeString (17:17) = '-' ELSE timeString = ' ' END IF string = TRIM(pathOut) // TRIM(timeString) // 'reservoirs.tab' CALL TableExport (tab, string ) !create new table for diverted discharge CALL TableNew ( tab ) !populate table string = 'diverted discharge' CALL TableSetId ( tab, string) IF ( PRESENT (time) ) THEN timeString = time string = 'diverted discharge at: ' // timeString ELSE string = 'diverted discharge at the end of simulation' END IF CALL TableSetTitle ( tab, string) !Allocate variables CALL TableSetRowCol ( tab, nReservoirsWithDiversion, 3 ) IF ( ALLOCATED ( row ) ) THEN DEALLOCATE ( row ) END IF ALLOCATE ( row (3) ) !set column header and unit CALL TableSetColHeader (tab, 1, 'id') CALL TableSetColHeader (tab, 2, 'Qin') CALL TableSetColHeader (tab, 3, 'Qout') CALL TableSetColUnit (tab, 1, '-') CALL TableSetColUnit (tab, 2, 'm3/s') CALL TableSetColUnit (tab, 3, 'm3/s') !fill in rows currentReservoir => pools DO i = 1, nReservoirs IF ( currentReservoir % bypassIsPresent ) THEN !id row (1) = ToString (currentReservoir % id) !Qin row (2) = ToString (currentReservoir % bypass % QinChannel) !Qout row (3) = ToString (currentReservoir % bypass % QoutChannel) currentReservoir => currentReservoir % next CALL TableFillRow (tab, i, row) END IF END DO IF (PRESENT(time)) THEN timeString = time timeString = timeString (1:19) // '_' timeString (14:14) = '-' timeString (17:17) = '-' ELSE timeString = ' ' END IF string = TRIM(pathOut) // TRIM(timeString) // 'reservoirs.tab' CALL TableExport (tab, string, append = .TRUE. ) RETURN END SUBROUTINE ReservoirSaveStatus !============================================================================== !| Description: ! Read reservoir stage and diversion discharge from file SUBROUTINE ReservoirReadStatus & ! (filename) IMPLICIT NONE !arguments with intent(in): CHARACTER ( LEN = *), INTENT (IN) :: filename ! local declarations: INTEGER (KIND = short) :: i CHARACTER (LEN = 300) :: string TYPE (TableCollection) :: tabs TYPE (Reservoir), POINTER :: currentReservoir !points to current reservoir !------------------------------end of declarations----------------------------- !read tables CALL TableNew ( filename, tabs ) !set reservoir stage currentReservoir => pools DO i = 1, nReservoirs string = TRIM ( ToString ( currentReservoir % id ) ) CALL TableGetValue ( valueIn = string, & tables = tabs, & id = 'reservoir stage', & keyIn = 'id', & keyOut = 'stage', & valueOut = currentReservoir % stage ) currentReservoir => currentReservoir % next END DO !set diversion discharge IF ( nReservoirsWithDiversion > 0 ) THEN currentReservoir => pools DO i = 1, nReservoirs IF ( currentReservoir % bypassIsPresent ) THEN string = TRIM ( ToString ( currentReservoir % id ) ) CALL TableGetValue ( valueIn = string, & tables = tabs, & id = 'diverted discharge', & keyIn = 'id', & keyOut = 'Qin', & valueOut = currentReservoir % bypass % QinChannel ) CALL TableGetValue ( valueIn = string, & tables = tabs, & id = 'diverted discharge', & keyIn = 'id', & keyOut = 'Qout', & valueOut = currentReservoir % bypass % QoutChannel ) END IF currentReservoir => currentReservoir % next END DO END IF RETURN END SUBROUTINE ReservoirReadStatus !============================================================================== !| Description: ! given a list of reservoirs, returns a pointer to one reservoir by id FUNCTION GetReservoirById & ( list, id) & RESULT (p) IMPLICIT NONE !Arguments with intent in: TYPE(Reservoir), POINTER, INTENT(IN) :: list !list of reservoirs INTEGER(KIND = short), INTENT(IN) :: id !Arguments with intent out: TYPE(Reservoir), POINTER :: p !pointer to reservoir !local arguments: LOGICAL :: found !------------end of declaration------------------------------------------------ !loop througout list of reservoirs searching for id p => list found = .false. DO WHILE (ASSOCIATED(p)) IF (p % id == id) THEN found = .TRUE. EXIT ELSE p => p % next END IF ENDDO IF (.NOT. found ) THEN CALL Catch ('error', 'Reservoirs', 'reservoir not found by & function GetReservoirById: ', argument = ToString(id)) END IF RETURN END FUNCTION GetReservoirById !============================================================================== !| Description: ! count number of reservoirs in a list FUNCTION CountReservoirs & ( list) & RESULT (c) IMPLICIT NONE !Arguments with intent in: TYPE(Reservoir), POINTER, INTENT(IN) :: list !list of reservoirs !Arguments with intent out: INTEGER(KIND = short) :: c !local arguments: TYPE(Reservoir), POINTER :: p !pointer to reservoir !------------end of declaration------------------------------------------------ !loop througout list of reservoirs searching for id p => list c = 0 DO WHILE (ASSOCIATED(p)) c = c + 1 p => p % next ENDDO RETURN END FUNCTION CountReservoirs !============================================================================== !| Description: ! populate array of daily values FUNCTION SetDailyArray & ! ( value ) & ! RESULT ( array ) IMPLICIT NONE !Arguments with intent(in): CHARACTER (LEN = *) :: value !Local declarations: REAL (KIND = float) :: array (365) REAL (KIND = float) :: scalar LOGICAL :: error TYPE (Table) :: valueTable INTEGER :: i REAL (KIND = float) :: doyStart, doyStop !----------------------------end of declarations------------------------------- !check that value is a number scalar = StringToFloat (value, error) IF ( error ) THEN !value changes in time CALL TableNew (value, valueTable) array = 0. DO i = 1, TableGetNrows (valueTable) CALL TableGetValue ( valueIn = REAL (i), & tab = valueTable, & keyIn = 'count', & keyOut ='doy-start', & match = 'exact', & valueOut = doyStart ) CALL TableGetValue ( valueIn = REAL (i), & tab = valueTable, & keyIn = 'count', & keyOut ='doy-stop', & match = 'exact', & valueOut = doyStop ) CALL TableGetValue ( valueIn = REAL (i), & tab = valueTable, & keyIn = 'count', & keyOut ='value', & match = 'exact', & valueOut = scalar ) array ( INT (doyStart) : INT (doyStop) ) = scalar END DO ELSE !no error, value is a scalar array = scalar END IF RETURN END FUNCTION SetDailyArray !============================================================================== !| Description: ! read geometry table SUBROUTINE ReadGeometry & ! (iniDB, k, reserv) IMPLICIT NONE !Arguments with intent(in): TYPE(IniList), INTENT (IN) :: iniDB INTEGER (KIND = short), INTENT (IN) :: k !Arguemnts with intent (inout): TYPE(Reservoir), POINTER, INTENT(INOUT) :: reserv !local declarations CHARACTER (LEN = 300) :: string INTEGER (KIND = short) :: nDOY INTEGER (KIND = short) :: shortInt INTEGER (KIND = short) :: i, j INTEGER(KIND = short), ALLOCATABLE :: doy (:) LOGICAL :: isString !---------------------------end of declarations-------------------------------- string = IniReadString ('geometry', iniDB, section = ToString(k)) CALL TableNew (string, reserv % geometry) nDOY = reserv % geometry % noCols - 3 ALLOCATE ( doy ( nDOY ) ) j = 0 DO i = 1, reserv % geometry % noCols shortInt = StringToShort ( reserv % geometry % col ( i ) % header, isString ) IF ( .NOT. isString ) THEN !number detected j = j + 1 doy ( j ) = shortInt END IF END DO !set when (doy day of year) geometry table changes reserv % geometryDOY (:) = MAXVAL (doy) ! DO i = 1, nDOY DO j = doy (i), 365 reserv % geometryDOY (j) = doy (i) END DO END DO DEALLOCATE (doy) RETURN END SUBROUTINE ReadGeometry !============================================================================== !| Description: ! read weir table SUBROUTINE ReadWeir & ! (iniDB, k, div) IMPLICIT NONE !Arguments with intent(in): TYPE(IniList), INTENT (IN) :: iniDB INTEGER (KIND = short), INTENT (IN) :: k !Arguemnts with intent (inout): TYPE(Diversion), INTENT(INOUT) :: div !local declarations CHARACTER (LEN = 300) :: string INTEGER (KIND = short) :: nDOY INTEGER (KIND = short) :: shortInt INTEGER (KIND = short) :: i, j INTEGER(KIND = short), ALLOCATABLE :: doy (:) LOGICAL :: isString !---------------------------end of declarations-------------------------------- string = IniReadString ('weir', iniDB, section = ToString(k), & subsection = 'diversion') CALL TableNew (string, div % weir) nDOY = div % weir % noCols - 1 ALLOCATE ( doy ( nDOY ) ) j = 0 DO i = 1, div % weir % noCols shortInt = StringToShort ( div % weir % col ( i ) % header, isString ) IF ( .NOT. isString ) THEN !number detected j = j + 1 doy ( j ) = shortInt END IF END DO !set when (doy day of year) geometry table changes div % weirDOY (:) = MAXVAL (doy) ! DO i = 1, nDOY DO j = doy (i), 365 div % weirDOY (j) = doy (i) END DO END DO DEALLOCATE (doy) RETURN END SUBROUTINE ReadWeir END MODULE Reservoirs